/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hu.arguscab.game;

import hu.arguscab.physic.Angle;
import hu.arguscab.physic.PhysicProperties;
import hu.arguscab.physic.Vector;
import hu.arguscab.physic.Vectors;

/**
 *
 * @author sharp
 */
public class ShipState implements State {

	private PhysicProperties props;

	private Angle dir;
	private Vector pos;
	private Vector force;;			// a rá ható eők eredője
	private Vector dirv;
	private Vector v;				// sebesség
	private Vector a;				// gyorsulás
	private float torque;
	private CompoundAction currentActions;
	private Ship ship;
	private boolean isCollision = false;	// volt-e ütközés EBBEN az állapiotban?
											// Az egyes hajoknak minden iteráció kezdetén
											// false-ra állítom.
	public ShipState(PhysicProperties p, Ship s) {
		this.props = p;
		this.pos = new Vector();
		this.dir = new Angle();
		this.dirv = new Vector(dir);
		this.force = new Vector();
		this.v = new Vector();
		this.a = new Vector();
		this.torque = 0;
		this.currentActions = new GameCompoundAction();
		this.ship = s;
	}

	public ShipState(ShipState old) {
		this.pos = old.getPos();
		this.props = old.props;
		this.dir = old.getDir();
		this.dirv = old.getDirv();
		this.force = old.getForce();
		this.v = old.getV();
		this.a = old.getA();
		this.torque = old.torque;
		this.currentActions = new GameCompoundAction();
		this.currentActions.copyActions( old.getCurrentActions() );
		this.ship = old.ship;
		this.isCollision = old.isCollision();
	}

	public ShipState(ShipState old, int tick) {
		this(old);
		calcPos(tick);
	}

	@Override
	public ShipState getCurrentState() {
		return new ShipState(this);
	}

	@Override
	public ShipState getState(int tick) {
		ShipState state = new ShipState(this, tick);
		state.update();
		return state;
	}

	@Override
	public ShipState getByActions(CompoundAction actions) {
		ShipState state = new ShipState(this);
		state.applyAction(actions);
		state.update();
		return state;
	}

	@Override
	public ShipState getByActions(CompoundAction actions, int tick) {
		ShipState state = new ShipState(this);
		for ( int i = 0; i < tick; ++i ) {
			state.applyAction(actions);
			state.update();
		}
		return state;
	}

	@Override
	public void applyAction(CompoundAction actions) {
		if ( actions == null )
			return;
		currentActions = actions;
		if ( actions.hasAction(GameAction.THROTTLE) )
			throttle(1);
		else if(actions.hasAction(GameAction.BREAK))
			throttle(-1);
		else
			a.setNull();
		if ( actions.hasAction(GameAction.LEFT) )
			steer(0);
		if ( actions.hasAction(GameAction.RIGHT) )
			steer(1);
	}

	@Override
	public void update() {
		calcPos();
		updatePos();
		updateDir();
	}

	@Override
	public void update(int tick) {
		for ( int i = 0; i < tick; ++i ) {
			calcPos();
			updatePos();
			updateDir();
		}
	}

	private void updatePos() {
		pos.add(v);
		checkLimits();
	}

	private void updateDir() {
		if (torque != 0) {
			dir.add(torque);
			dirv.setByAngle( dir );
			// fordulás légellenállása
			torque *= 0.95;
			if ( Math.abs(torque) < 0.002 )
				torque = 0;
		}
	}

	private void calcPos() {
		calc(force, a, v);
	}

	private void calcPos(int tick) {
		for ( int i = 0; i < tick; ++i )
			calc(force, a, v);
	}

	/**
	 * v[n] = v[n-1] + a*t;
	 * a = force / m;
	 * @param force
	 * @param v
	 */
	private void calc(final Vector force, final Vector a, final Vector v) {
		//applyForce( new Vector(0, 0.01f) );		// gravitáció
		// légellenállás
		if ( currentActions.hasAction(GameAction.THROTTLE) == false ) {
			float len = v.getLength();
			if ( len < 0.0002 ) {
				force.setNull();
				v.setNull();
			} else {
				Vector legEllenallas = Vectors.normalize(v);
				v.setLength( len*0.95f );
				//force.add( Vectors.multiply(legEllenallas, -len*0.1f) ); // legellenallas
			}
		}
		// max gyorsulás
		a.checkAndSetMaxLength(props.getMaxSpeed());
		// max erő
		force.checkAndSetMaxLength(props.getMaxSpeed());
		
		// Sum(F)
		Vector F = new Vector( Vectors.add(a, force) );

		v.add( Vectors.divide(F, props.getMass()) );
		// max sebesség
		v.checkAndSetMaxLength(props.getMaxSpeed());
	}

	/**
	 * @TODO: át a propertiesbe!
	 * @param i
	 */
	private void throttle(int i) {
		//applyForce( Vectors.multiply(dirv, props.getRocketPower()*i) );
		this.a.add(Vectors.multiply(dirv, props.getRocketPower() * i));
	}

	/**
	 * @TODO: át a propertiesbe!
	 */
	private void steer(int dir) {
		if (dir == 0) { // balra
			torque -= props.getTurningSpeed();
		} else
			torque += props.getTurningSpeed();
		torque = Math.min(Math.max(torque, -props.getMaxTurningSpeed()),	// [-max, torque]
						  props.getMaxTurningSpeed());						// [torque, max]
	}

	public void setPos(Vector pos) {
		this.pos = pos;
	}

	public Vector getPos() {
		return new Vector(pos);
	}

	public Vector getA() {
		return new Vector(a);
	}

	public Angle getDir() {
		return new Angle(dir);
	}

	public Vector getDirv() {
		return new Vector(dirv);
	}

	public Vector getForce() {
		return new Vector(force);
	}

	public PhysicProperties getProps() {
		return props;
	}

	public float getTorque() {
		return torque;
	}

	public Vector getV() {
		return new Vector(v);
	}

	private void checkLimits() {
		// Unit Testeknél ez lehet null
		Map map = GameInfo.instance().getMap();
		//Map map = new Map();
		//map.setWidth(TestScene.WIDTH);
		//map.setHeight(TestScene.HEIGHT);
		//if ( GameInfo.instance().getMap() == null)
		//	return;
		if ( pos.getY() < 0 || pos.getY() > map.getHeight()) {
			v.setY( -v.getY() );
			isCollision = true;
		}
		if ( pos.getX() < 0 || pos.getX() > map.getWidth()) {
			v.setX( -v.getX() );
			isCollision = true;
		}
	}

	private CompoundAction getCurrentActions() {
		return currentActions;
	}

	public CompoundAction getCurrentActionsCopy() {
		GameCompoundAction ac = new GameCompoundAction();
		ac.copyActions(currentActions);
		return ac;
	}

	@Override
	public String toString() {

		return "Pos: " + pos +
		", Acc: " + a +
		", Dir: " + dir;
	}

	public Ship getShip() {
		return ship;
	}

	public void setShip(Ship ship) {
		this.ship = ship;
	}

	public boolean isCollision() {
		return isCollision;
	}

	public void setCollision(boolean b) {
		isCollision = b;
	}
}
